#include "planner/child_property_deriver.h"

#include <utility>
#include <vector>

#include "planner/group_expression.h"
#include "planner/memo.h"
#include "planner/physical_operators.h"
#include "planner/properties.h"
#include "planner/property_set.h"

extern "C" {
#include <nodes/pg_list.h>
}

namespace optimizer {

std::vector<std::pair<PropertySet *, std::vector<PropertySet *>>> ChildPropertyDeriver::GetProperties(
    Memo *memo, PropertySet *requirements, GroupExpression *gexpr) {
  requirements_ = requirements;
  output_.clear();
  memo_ = memo;
  gexpr_ = gexpr;

#define OPERATOR_TYPE_ENUM(T)      \
                                   \
  T(OperatorType, PhysicalSeqScan) \
  T(OperatorType, OrderBy)         \
  T(OperatorType, Limit)

  switch (gexpr->Contents()->GetOpType()) {
#define ENUM_VISIT(EnumType, EnumVal)             \
  case EnumType::EnumVal: {                       \
    Visit(&gexpr->Contents()->CastTo<EnumVal>()); \
  } break;
    OPERATOR_TYPE_ENUM(ENUM_VISIT);

#undef ENUM_VISIT
    default:
      throw std::runtime_error("Unsupported operator type for child property deriver");
  }

#undef OPERATOR_TYPE_ENUM

  return std::move(output_);
}

void ChildPropertyDeriver::Visit(UNUSED_ATTRIBUTE PhysicalSeqScan *op) {
  // Seq Scan does not provide any property
  output_.emplace_back(new PropertySet(), std::vector<PropertySet *>{});
}

void ChildPropertyDeriver::Visit(Limit *op) {
  // Limit fulfill the internal sort property
  std::vector<PropertySet *> child_input_properties{new PropertySet()};
  auto *provided_prop = new PropertySet();
  if (list_length(op->sort_exprs_) != 0) {
    const std::vector<OrderByOrderingType> &sorts{op->sort_directions_};
    provided_prop->AddProperty(new PropertySort(copyObject(op->sort_exprs_), sorts));
  }

  output_.emplace_back(provided_prop, std::move(child_input_properties));
}

void ChildPropertyDeriver::Visit(UNUSED_ATTRIBUTE OrderBy *op) {}

}  // namespace optimizer
